﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DAL = Medianamik.SubsonicDALProvider.DAL;
using Medianamik.Core;
using SubSonic;
using Medianamik.SubsonicDALProvider;
using System.Data;

namespace Medianamik.SubsonicDALProvider
{
    public class SubsonicDALProvider : Medianamik.Core.DAL.IDALProvider
    {
        #region Methods

        #region Public

        #region INodeDALProvider Members

        public Core.Node GetNode(Guid pID, int pRevision)
        {
            DAL.Node node = new DAL.Node(pID);

            DataSet properties = DAL.SPs.GetNodePropertyValues(pID, pRevision).GetDataSet();

            //TODO : Trouver des messages d'exception appropriés
            if(properties.Tables.Count < 1)
                throw new Exception();

            if(properties.Tables[0].Rows.Count < 1)
                 throw new Exception();

            //TODO : Récupérer une collection de propriétés et ses valeurs (NodeProperty)
            return SubsonicConverter.FromDALNode(node, properties.Tables[0]);
        }

        public Core.Node GetNode(Guid pID)
        {
            throw new NotImplementedException();
        }

        public Core.Node GetPublishedNode(Guid pID)
        {
            throw new NotImplementedException();
        }

        public List<Core.Node> GetChildNodes(Guid pID, bool inclusive, int depth)
        {
            throw new NotImplementedException();
        }

        public List<Core.Node> GetChildNodes(Guid pID, bool inclusive)
        {
            throw new NotImplementedException();
        }

        public List<Core.Node> GetChildNodes(Guid pID)
        {
            throw new NotImplementedException();
        }

        public bool SaveNode(Core.Node pNode)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region INodeTypeDALProvideer Members

        /// <summary>
        /// Charge un Type Medianamik en fonction de son TypeID
        /// </summary>
        /// <param name="pTypeID">Le TypeID (Guid)</param>
        /// <returns>Un object de type NodeType</returns>
        public NodeType GetNodeType(Guid pTypeID)
        {
            //return AllTypes.Find(t => t.ID.Equals(pTypeID));

            DAL.Type DALType = new DAL.Type(pTypeID);
            DAL.PropertyXCollection DALProperies = new DAL.PropertyXCollection()
                .Where(DAL.PropertyX.Columns.TypeId, pTypeID)
                .Load();

            return SubsonicConverter.FromDALType(DALType, DALProperies);

        }

        /// <summary>
        /// Charge un Type Medianamik en fonction de son TypeName
        /// </summary>
        /// <param name="pTypeID">Le TypeName</param>
        /// <returns>Un object de type NodeType</returns>
        public NodeType GetNodeType(string pTypeName)
        {
            //return AllTypes.Find(t => t.Name.Equals(pTypeName, StringComparison.OrdinalIgnoreCase));

            DAL.Type DALType = new DAL.Type(DAL.Type.Columns.Name, pTypeName);
            DAL.PropertyXCollection DALProperies = new DAL.PropertyXCollection()
                .Where(DAL.PropertyX.Columns.TypeId, DALType.TypeId)
                .Load();

            return SubsonicConverter.FromDALType(DALType, DALProperies);
        }

        /// <summary>
        /// Charge une liste de Types Medianamik en fonction de leur ParentTypeID
        /// Si le ParentTypeID est null, les Types racine seront chargés
        /// </summary>
        /// <param name="pTypeID">Le TypeID du Parent (Guid)</param>
        /// <returns>Une list d'objets de type NodeType</returns>
        public List<NodeType> GetChildTypes(Guid? pParentTypeID)
        {
            //return AllTypes.FindAll(t => t.ParentID.Equals(pParentTypeID));

            List<NodeType> result = new List<NodeType>();
            DAL.TypeCollection DALTypes = new DAL.TypeCollection()
                .Where(DAL.Type.Columns.ParentTypeId, pParentTypeID)
                .Load();

            if (DALTypes.Count == 0)
            {
                //Pas la peine de continuer, on retourne la collection vide
                return result;
            }

            SubSonic.Query q = DAL.PropertyX.Query();
            q.IN(DAL.PropertyX.Columns.TypeId, DALTypes.ConvertAll<object>(t => t.TypeId).ToArray());

            DAL.PropertyXCollection DALProperies = new DAL.PropertyXCollection();
            DALProperies.Load(q.ExecuteReader());

            DALTypes.ForEach(delegate(DAL.Type DALType)
            {

                NodeType BOLType = SubsonicConverter.FromDALType(DALType, DALProperies.FindAll(p => p.TypeId.Equals(DALType.TypeId)));
                result.Add(BOLType);
            });

            return result;

        }

        /// <summary>
        /// Charge une liste de Types Medianamik en fonction de leur ParentTypeName
        /// </summary>
        /// <param name="pTypeID">Le TypeName du Parent</param>
        /// <returns>Une list d'objets de type NodeType</returns>
        public List<NodeType> GetChildTypes(string pTypeName)
        {
            //List<NodeType> result = new List<NodeType>();
            //NodeType parentType = GetNodeType(pTypeName);
            //if (parentType != null)
            //{
            //    result.AddRange(AllTypes.FindAll(t => t.ParentID.Equals(parentType.ID)));
            //}
            //else
            //{
            //    //return Collection will be empty
            //}
            //return result;

            List<NodeType> result = new List<NodeType>();
            NodeType parentType = GetNodeType(pTypeName);

            DAL.TypeCollection DALTypes = new DAL.TypeCollection()
                .Where(DAL.Type.Columns.ParentTypeId, parentType.ID)
                .Load();

            if (DALTypes.Count == 0)
            {
                //Pas la peine de continuer, on retourne la collection vide
                return result;
            }

            SubSonic.Query q = DAL.PropertyX.Query();
            q.IN(DAL.PropertyX.Columns.TypeId, DALTypes.ConvertAll<object>(t => t.TypeId).ToArray());

            DAL.PropertyXCollection DALProperies = new DAL.PropertyXCollection();
            DALProperies.Load(q.ExecuteReader());

            DALTypes.ForEach(delegate(DAL.Type DALType)
            {

                NodeType BOLType = SubsonicConverter.FromDALType(DALType, DALProperies.FindAll(p => p.TypeId.Equals(DALType.TypeId)));
                result.Add(BOLType);
            });

            return result;

        }

        /// <summary>
        /// Enregistre un NodeType dans un BD
        /// Cette méthode peut être utilisée pour les opérations de type Insert ou Update
        /// </summary>
        /// <param name="pType">Un object de type NodeType à sauvegarder</param>
        /// <returns>L'objet NodeType passé en paramètre</returns>
        public NodeType SaveType(NodeType pType)
        {
            if (pType.IsNew)
            {
                return InsertType(pType);
            }
            else
            {
                return UpdateType(pType);
            }

        }

        /// <summary>
        /// Vérifie l'existence d'un Type Medianamik avec le TypeName passé en paramètre.
        /// </summary>
        /// <param name="pTypeName">Le TypeName à vérifier</param>
        /// <returns>True si le Type existe, sinon False</returns>
        public bool Exists(string pTypeName)
        {

            // return AllTypes.Exists(t => t.Name.Equals(pTypeName, StringComparison.OrdinalIgnoreCase));

            SubSonic.Query q = DAL.Type.Query();
            q.WHERE(DAL.Type.Columns.Name, pTypeName);

            return q.GetRecordCount() == 1;

        }

        #endregion

        #endregion

        #region private

        /// <summary>
        /// Met à jour un Type Medianamik et ses Properties dans la BD
        /// </summary>
        /// <param name="pType">Le NodeType à mettre à jour</param>
        /// <returns>Le NodeType passé en paramètre</returns>
        private NodeType UpdateType(NodeType pType)
        {
            if (pType.IsNew)
            {
                throw new ArgumentException("pType cannot have IsNew=true");
            }

            DAL.PropertyXCollection DALPRoperties = null;
            DAL.Type DALType = SubsonicConverter.ToDALType(pType, out DALPRoperties);

            DALType.Save();
            DALPRoperties.SaveAll();

            return pType;
        }

        /// <summary>
        /// Insère un Type Medianamik et ses Properties dans la BD
        /// </summary>
        /// <param name="pType">Le NodeType à insérer</param>
        /// <returns>Le NodeType passé en paramètre. Son TypeID a été mis à jour</returns>
        private NodeType InsertType(NodeType pType)
        {
            if (pType.Properties.Exists(p => !p.IsNew))
            {
                throw new Exception("New Type must have new Properties");
            }

            //Debug.Assert(pType.Properties.TrueForAll(p => p.TypeID.Equals(pType.ID)));

            DAL.PropertyXCollection DALProperties = null;
            DAL.Type DALType = SubsonicConverter.ToDALType(pType, out DALProperties);

            //Save new Type and get its new ID
            DALType.Save();
            //Debug.Assert(!DALType.TypeId.Equals(Guid.Empty));

            DALProperties.ForEach(p => p.TypeId = DALType.TypeId);
            DALProperties.SaveAll();

            pType.ID = DALType.TypeId;
            return pType;
        }

        #endregion

        #endregion
    }
}
